#include "Stream.h"

#include <string.h> // memcpy


namespace LightPlayer{

	Stream::Stream( const uint8* i_pData, const uint32 i_iBufferSize ){
		m_pBuffer = i_pData;
		m_iBufferSize = i_iBufferSize;
		m_pCurrentPosition = m_pBuffer;
		align();
	}

	void Stream::init( const uint8* i_pData /*= NULL*/, const uint32 i_iBufferSize /*= 0 */ ){
		m_pBuffer = i_pData;
		m_iBufferSize = i_iBufferSize;
		m_pCurrentPosition = m_pBuffer;
		align();
	}

	const uint8* Stream::moveOf( uint32 numberOfByte ){
		assert( getCurrentOffset() + numberOfByte <= m_iBufferSize );
		const uint8* tmp = m_pCurrentPosition;
		m_pCurrentPosition += numberOfByte;
		return tmp;
	}

	bool Stream::read_bool(){
		return read_uint(1) ? true : false;
	}

	float Stream::read_fixed16(){
		align();
		int16 val = read_s16();
		return (float) val / 256.0f;
	}

	float Stream::read_fixed(){
		align();
		int32 val = read_s32();
		return (float) val / 65536.0f;
	}

	float	Stream::read_float16(){
		align();
		uint16 val = deref_as( uint16, moveOf(2) );
		uint32 x = (val & 0x8000) << 16;
		int exponent = (val & 0x7C00) >> 10;
		// Special Values
		if (exponent)
		{
			x |= (exponent + (127 - 16)) << 23;	// exponent
		}
		// mantissa
		x |= (val & 0x3FF) << 13;
		float f;
		assert(sizeof(float) == sizeof(uint32));
		memcpy(&f, &x, sizeof(float));
		return f;
	}

	float	Stream::read_float(){
		align();
		float f = deref_as( float, moveOf(4) );
		return f;
	}

	double	Stream::read_double(){
		align();
		double f = deref_as( double, moveOf(8) );
		return f;
	}

	uint8 Stream::read_u8(){
		align();
		uint8 v = deref_as( uint8, moveOf(1) );
		return v;
	}

	int8 Stream::read_s8(){
		align();
		int8 v = deref_as( int8, moveOf(1) );
		return v;
	}

	uint16 Stream::read_u16(){
		align();
		uint16 v = deref_as( uint16, moveOf(2) );
		return v;
	}

	int16 Stream::read_s16(){
		align();
		int16 v = deref_as( int16, moveOf(2) );
		return v;
	}

	uint32 Stream::read_u32(){
		align();
		uint32 v = deref_as( uint32, moveOf(4) );
		return v;
	}

	int32 Stream::read_s32(){
		align();
		int32 v = deref_as( int32, moveOf(4) );
		return v;
	}

	uint32	Stream::read_uint(int32 bitcount){
		assert(bitcount <= 32 && bitcount >= 0);

		uint32	value = 0;
		int	bits_needed = bitcount;
		while (bits_needed > 0){
			if (m_unused_bits) {
				if (bits_needed >= m_unused_bits) {
					// Consume all the unused bits.
					value |= (m_current_byte << (bits_needed - m_unused_bits));
					bits_needed -= m_unused_bits;
					m_current_byte = 0;
					m_unused_bits = 0;

				} else {
					// Consume some of the unused bits.
					value |= (m_current_byte >> (m_unused_bits - bits_needed));
					// mask off the bits we consumed.
					m_current_byte &= ((1 << (m_unused_bits - bits_needed)) - 1);
					m_unused_bits -= bits_needed;
					// We're done.
					bits_needed = 0;
				}
			} else {
				m_current_byte = deref_as( uint8, moveOf(1) );
				m_unused_bits = 8;
			}
		}
		assert(bits_needed == 0);
		return value;
	}

	int32 Stream::read_sint(int32 bitcount){
		assert(bitcount <= 32 && bitcount >= 0);
		int32 value = (int32) read_uint(bitcount);
		// Sign extend...
		if (value & (1 << (bitcount - 1))) {
			value |= -1 << bitcount;
		}
		return value;
	}

	void Stream::skip_byte( int32 byte_count ){
		align();
		moveOf(byte_count);
	}

};